/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#ifndef _NAS_SHARE_HETRO_COMMON_SERVICE_H
#define _NAS_SHARE_HETRO_COMMON_SERVICE_H
#define DEVSTUB

/* System Header files */
#include <atomic>
#include <map>
#include <memory>
#include <arpa/inet.h>

/* Module Header files */
#include "define/Types.h"
#include "common/Thread.h"
#include "common/JsonHelper.h"
#include "common/Path.h"
#include "common/CleanMemPwd.h"
#include "device_access/DeviceManager.h"
#include "device_access/ControlDevice.h"

/* Plugin Framework Header files */
#include "thrift_interface/ApplicationProtectBaseDataType_types.h"
#include "thrift_interface/ApplicationProtectPlugin_types.h"
#include "thrift_interface/ApplicationProtectFramework_types.h"
#include "job/BasicJob.h"
#include "job/JobCommonInfo.h"
#include "job_mgr/JobMgr.h"
#include "client/ClientInvoke.h"

/* Nas Service Header files */
#include "common/constant/ErrorCode.h"
#include "common/constant/PluginNasTypes.h"
#include "common/constant/PluginServiceCommon.h"
#include "utils/PluginUtilities.h"

/* Scanner module */
#include "model/ScanConfig.h"

/* Scanner "HetroCommonService" */
#include "ScanMgr.h"

/* Backup module */
#include "BackupMgr.h"
#include "SmbContextWrapper.h"

/* Hetro Common Structs */
#include "HetroCommonStructs.h"

struct TaskInfo {
    int64_t m_lastLogReportTime = 0;         /* Last when we reported report to PM */
};

// used to track agent UBC rpc call duration
template<typename T, typename F1, typename F2>
static void InterfaceInvokeTimer(std::chrono::duration<T> timeout, F1 procedure, F2 timeoutCallback)
{
    auto begin = std::chrono::steady_clock::now();
    procedure();
    auto end = std::chrono::steady_clock::now();
    if (end - begin >= timeout) {
        timeoutCallback();
    }
}

class NodeLevelTaskInfo {
public:
    void Insert(const std::string mainTaskId)
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter  Insert" << HCPENDLOG;
        struct TaskInfo info{};
        std::lock_guard<std::shared_mutex> lck(m_mtxLock);
        if (CheckKeyExist(mainTaskId)) {
            return;
        }
        m_mainTaskMap.emplace(mainTaskId, info);
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Exit Insert" << HCPENDLOG;
        return;
    }

    void Erase(std::string mainTaskId)
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter Erase" << HCPENDLOG;
        std::lock_guard<std::shared_mutex> lck(m_mtxLock);
        if (!CheckKeyExist(mainTaskId)) {
            return;
        }
        m_mainTaskMap.erase(mainTaskId);
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Exit Erase" << HCPENDLOG;
        return;
    }

    void Clear(const std::string mainTaskId)
    {
        DBGLOG("Clear task %s", mainTaskId.c_str());
        std::lock_guard<std::shared_mutex> lck(m_mtxLock);
        m_mainTaskMap.clear();
    }
    bool CheckKeyExist(const std::string &key)
    {
        return m_mainTaskMap.count(key) != 0;
    }

    void IncrSubTasksCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter IncrSubTasksCount" << HCPENDLOG;
        m_noOfCurrRunningSubTasks++;
        m_noOfTotalSubTasks++;
    }

    void DecrSubTasksCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter DecrSubTasksCount" << HCPENDLOG;
        if (m_noOfCurrRunningSubTasks != 0) {
            m_noOfCurrRunningSubTasks--;
        }
    }

    uint64_t GetSubTasksCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter GetSubTasksCount" << HCPENDLOG;
        return m_noOfTotalSubTasks;
    }

    uint64_t GetRunningSubTasksCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter GetRunningSubTasksCount" << HCPENDLOG;
        return m_noOfCurrRunningSubTasks;
    }

    bool CanSendLogReportToPM(const std::string mainTaskId)
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter CanSendLogReportToPM" << HCPENDLOG;
        std::lock_guard<std::shared_mutex> lck(m_mtxLock);
        if ((PluginUtils::GetCurrentTimeInSeconds() - m_mainTaskMap[mainTaskId].m_lastLogReportTime) <
            BACKUP_REPORT_CIRCLE_TIME_IN_SEC) {
            HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Exit CanSendLogReportToPM ret false" << HCPENDLOG;
            return false;
        }
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Exit CanSendLogReportToPM ret true" << HCPENDLOG;
        return true;
    }

    bool UpdateLogReportTimeToPM(const std::string mainTaskId)
    {
        std::lock_guard<std::shared_mutex> lck(m_mtxLock);
        m_mainTaskMap[mainTaskId].m_lastLogReportTime = PluginUtils::GetCurrentTimeInSeconds();
        return true;
    }

    void IncrSubTasksSubmitCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter IncrSubTasksSubmitCount" << HCPENDLOG;
        m_noOfSubTasksGenerated++;
    }

    uint64_t GetSubTasksSubmitCount()
    {
        HCP_Log(DEBUG, MODULECOM_HETRO_COMMON) << "Enter GetRunningSubTasksCount" << HCPENDLOG;
        return m_noOfSubTasksGenerated;
    }

private:
    std::shared_mutex m_mtxLock {};                     /* mutex lock */
    std::map<std::string, struct TaskInfo> m_mainTaskMap {};  /* node+main-task-id level info */
    std::atomic<uint64_t> m_noOfCurrRunningSubTasks = 0;    /* node level info - no of sub-tasks currently running */
    std::atomic<uint64_t> m_noOfTotalSubTasks = 0;     /* node level info - total no of sub-tasks (rung + cmpltd) */
    std::atomic<uint64_t> m_noOfSubTasksGenerated = 0;
};
extern NodeLevelTaskInfo g_nodeLevelTaskInfo;

class HetroCommonService : public BasicJob {
public:
    HetroCommonService();
    virtual ~HetroCommonService() {};
    virtual int PrerequisiteJob() = 0;
    virtual int GenerateSubJob() = 0;
    virtual int ExecuteSubJob() = 0;
    virtual int PostJob() = 0;

    bool GetSubJobTypeByFileName(const std::string &fileName, std::string &subTaskName,
                                            uint32_t &subTaskType, uint32_t &subTaskPrio);

    bool InitSubTask(SubJob &subJob, const std::string& jobId,
        const std::string& ctrlFile, uint32_t &subTaskType, const std::string& ext, const std::string& parentDir = "");

    bool IsValidCtrlFile(uint32_t subTaskType, std::string ctrlFileFullPath);

    /**
     * API to create a sub-task
     */
    int CreateSubTask(std::vector<SubJob> &subJobList, std::vector<std::string> &ctrlFileList);
    int CreateSubTask(const SubJob &job);
    bool CreateCheckSubTask();

    /**
     * API to Report job logs to DME_UBC/PM
     */
    bool ReportJobDetails(const SubJobDetails& jobInfo);

    /**
     * APIs to read/write backupCopyMetaInfo to the backup-copy-meta.json file
     */
    bool ReadBackupCopyFromFile(std::string metaFileFullPath, HetroLastCopyDetails &backupCopy);
    bool WriteBackupCopyToFile(std::string metaFileFullPath, HetroLastCopyDetails &backupCopy);

    /**
     * APIs to create/get/update/delete shared resource to store genetal info of a job
     */
    bool LockGeneralResource(std::string mainJobId);
    bool UnlockGeneralResource(std::string mainJobId);
    bool CreateGeneralResource(std::string key, HetroNativeGeneral &generalInfo);
    bool GetGeneralResource(std::string key, HetroNativeGeneral &generalInfo);
    bool UpdateGeneralResource(std::string key, HetroNativeGeneral &generalInfo);
    bool UpdateGeneralResourceStub(std::string key, HetroNativeGeneral &generalInfo);
    bool DeleteGeneralResource(std::string key);

    /**
     * APIs to create/get/update/delete shared resource to store scanner statistics of a job
     */
    bool CreateScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats);
    bool GetScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats);
    bool UpdateScanStatsResource(std::string key, HetroNativeScanStatistics &scanStats);
    bool UpdateScanStatsResourceStub(std::string key, HetroNativeScanStatistics &scanStats);
    bool DeleteScanStatsResource(std::string key);

    /**
     * APIs to create/get/update/delete shared resource to store backup statistics of a job
     */
    bool LockBackupStatsResource(std::string mainJobId);
    bool UnlockBackupStatsResource(std::string mainJobId);
    bool CreateBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats);
    bool GetBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats);
    bool UpdateBackupStatsResource(std::string key, HetroNativeBackupStats &backupStats);
    bool DeleteBackupStatsResource(std::string key);

    /**
     * APIs to get/update/delete shared resource to store backup sub task count of a job
     */
    bool WriteBackupStatsToFile(std::string filePath, HetroNativeBackupStats &backupStatistics);
    bool ReadBackupStatsFromFile(std::string filePath, HetroNativeBackupStats &backupStatistics);

    /**
     * API to clean anthentication info file, ticket, keytab, krb5config ...
     */
    void CleanKrbFilesForCifs(std::string jobId);
    ScanStatistics AddScanStatistics(const ScanStatistics& stats_A, const ScanStatistics& stats_B);

    void SetJobCtrlPhase(std::string jobCtrlPhase);
    static uint64_t GetCurrentTimeFromRemoteServerWithMsg(std::string path, std::string &errMsg);
    static uint64_t GetCurrentTimeFromRemoteServer(std::string path);
    void SetMainJobId(const std::string& jobId);
    void SetSubJobId();

    static bool CreateResource(const std::string key, const std::string &val,
        const std::string& mainJobId = "", const std::string& subJobId = "");
    static bool QueryResource(const std::string key, AppProtect::ResourceStatus &ret,
        const std::string& mainJobId = "", const std::string& subJobId = "");
    static bool UpdateResource(const std::string key, Resource &res,
        const std::string& mainJobId = "", const std::string& subJobId = "");
    static bool DeleteResource(const std::string key,
        const std::string& mainJobId = "", const std::string& subJobId = "");
    static bool LockResource(const std::string key, const uint32_t maxRetryCount, const std::string& mainJobId);
    static bool UnlockResource(const std::string key, const uint32_t maxRetryCount, const std::string& mainJobId);

    bool CreateSharedResources(std::string jobId, HetroNativeGeneral &generalInfo,
        HetroNativeScanStatistics &scanStats, HetroNativeBackupStats &backupStats);

    bool GetSharedResources(std::string jobId, HetroNativeGeneral &generalInfo,
        HetroNativeScanStatistics &scanStats, HetroNativeBackupStats &backupStats);

    bool DeleteSharedResources(std::string jobId);
    void FillBackupSmbConfigAuthInfo(BackupParams &backupParams, std::string &jobId,
        StorageRepository &m_dataFs, ProtectedNasShare &m_nasShare, BackupType jobType);
    bool CheckDataFsMount(std::string proto, std::string protoVersion,
        StorageRepository &m_dataFs, std::string m_dataFsSvcIp, ProtectedNasShare &m_nasShare);

    bool UpdateScannerStatistics(HetroNativeScanStatistics &scanStatistics,
        HetroNativeScanStatistics &scanStats, bool isScannerRestarted, std::string &jobId);

    void PrintBackupStatistics(BackupStats &backupStatistics,
        std::string &jobId, BackupPhaseStatus m_backupStatus, std::string &statPrintSubJobId);

    bool UpdateBackupSubTaskStatistics(BackupStats &backupStatistics,
        uint32_t &subTaskType, std::string &metaFsPath, std::shared_ptr<SubJob> &subJobInfo);

    void PrintSubJobInfo(std::shared_ptr<SubJob> &m_subJobInfo);

    bool IsUpdateBackupStartTimeRequired(HetroBackupSubJob& backupSubJob,
        HetroNativeGeneral &generalInfo);

    bool PrintFinalBackupStats(HetroNativeBackupStats &backupStatistics);

    bool PrintFinalScannerStats(HetroNativeScanStatistics &scanStats);

    void PrintBackupScanFilter(ScanDirectoryFilter dirFilter, ScanFileFilter fileFilter);

    void FillScanConfigForFilter(std::vector<AppProtect::ResourceFilter> filters, ScanConfig &scanConfig);
    FILTER_TYPE GetFilterType(std::string mode);
    virtual void FillScanConfigForNative(ScanConfig &scanConfig);
    virtual void FillScanConfigForAggr(ScanConfig &scanConfig);

    static int ChangeHostnameToIp(std::string& hostname, const std::string& protocol);
    static bool CheckIpPortConnection(const std::string& ip, const std::string& port);
    bool checkFilePathAndGetSrcFileList(std::string srcDir, std::string dstDir,
        std::vector<std::string> &srcFileList);
    bool CheckRemoteNasMount(std::string proto, std::string protoVersion,
        ProtectedNasShare &m_nasShare, std::string m_tempLocalMountPath);

    static Module::SmbVersion ConvertStringToSmbVersion(std::string versionString);
    static std::string ConvertSmbVersionToString(Module::SmbVersion smbVersion);

    /**
     * Query snapshot for NAS
     */
    static bool OceanStorV5SnapshotExists(ProtectedNasHost nasHost, ProtectedNasShare nasShare,
        const std::string& snapshotName);

    /**
     * Create snapshot for a NAS Share
     */
    static bool CreateSnapshot(ProtectedNasHost nasHost,
                               ProtectedNasShare nasShare,
                               std::string &outSnapshotName,
                               uint64_t &outSnaphotCreatedTime,
                               std::string &outSnapshotNasSharePath,
                               int64_t &homoErrCode);

    /**
     * Delete snapshot of a NAS Share
     */
    static bool DeleteSnapshot(ProtectedNasHost nasHost, ProtectedNasShare nasShare,
        std::string snapshotName, int64_t &homoErrCode);

    static void CreateSnapshotError(std::string &outSnapshotName, uint64_t &outSnaphotCreatedTime,
                                    std::string &outSnapshotNasSharePath);
    /* CA证书管理 */
    bool CertVerifyMgr(const AppProtect::ApplicationEnvironment& protectEnv,
        ProtectedNasHost& nasHost);
    bool RemoveCertification(const AppProtect::ApplicationEnvironment& protectEnv);

    /* 上报任务状态，保活 */
    static void KeepJobAlive(const std::string& mainJobId, const std::string& subJobId);

    void SleepForCreateSubTaskError();
    bool SendJobReportForAliveness();
    bool CheckAndRetryCreateSubTask(std::vector<SubJob> &subJobList,
        std::vector<std::string> &ctrlFileList, int64_t &createJobErrTime, bool isFinal);
    bool CreateBackupJobTaskToCreateFurtherSubTasks();
    void MergeBackupFailureRecords(const std::string& src_output_failure_record_dir);
    void RegisterScanTask();
    void ReleaseScanTask();
    void RemoveSubJobFromChannelMgr();
    void AddSubJobToChannelMgr();

protected:
    uint64_t m_curProcess = 0;
    std::string m_subJobId;
    std::atomic<int> m_jobProgress {0};
    std::atomic<double> m_dataSize {0.0};
    std::string m_jobCtrlPhase;

    /* Scanner instance */
    std::unique_ptr<Scanner> m_scanner { nullptr };

    /* Subtask completion status of scan task */
    SCANNER_STATUS m_scanStatus {};

    uint32_t m_orderNumberForAggregate {0};
    // 判断是否是聚合格式
    bool m_aggregateRestore {false};
    // 判断是否为细粒度恢复
    bool m_fineGrainedRestore {false};

    bool m_justRestoreByScan {false};

    bool m_tapeCopy {false};

    int64_t m_lastJobReportTime {0};

    /* Root Path Of Backup Failure Recorder Output */
    std::string m_failureRecordRoot {};
    uint64_t m_maxFailureRecordsNum = 0;

    std::vector<std::string> m_IpRuleList;
    std::vector<std::string> m_remoteIpRuleList;
    std::string m_deviceId;
    ScanMgr& m_scanMgr;
 
    void PrintScannerStatistics(HetroNativeScanStatistics &scanStatistics);

    template<typename... Args>
    void ReportJobDetailsWithLabelAndErrcode(const JobLogLevel::type &logLevel, SubJobStatus::type jobStatus,
        int jobProgress, std::string logLabel, int64_t errCode, Args... logArgs)
    {
        SubJobDetails subJobDetails;
        LogDetail logDetail {};
        std::vector<LogDetail> logDetailList;
        ActionResult result;
        int32_t jobSpeed = 0;

        if (IsAbortJob() && jobStatus == SubJobStatus::RUNNING) {
            INFOLOG("Job is aborted, force change jobStatus to aborting");
            jobStatus = SubJobStatus::ABORTING;
            logLabel = "";
        }

        AddLogDetail(logDetail, logLabel, logLevel, logArgs...);

        AddErrCode(logDetail, errCode);

        if (m_dataSize != 0) {
            subJobDetails.__set_dataSize(m_dataSize);
        }

        INFOLOG("Enter ReportJobDetails: jobId: %s, subJobId: %s, jobStatus: %d, logLabel: %s, errCode: %d",
            m_jobId.c_str(), m_subJobId.c_str(), static_cast<int>(jobStatus), logLabel.c_str(), errCode);

        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, jobProgress, jobSpeed, jobStatus);
        if (result.code != Module::SUCCESS) {
            ERRLOG("ReportJobDetails failed, jobId: %s, subJobId: %s, errCode: %d",
                m_jobId.c_str(), m_subJobId.c_str(), result.code);
        }

        INFOLOG("Exit ReportJobDetails: jobId: %s, subJobId: %s, jobStatus: %d, logLabel: %s, errCode: %d",
            m_jobId.c_str(), m_subJobId.c_str(), static_cast<int>(jobStatus), logLabel.c_str(), errCode);
        return;
    }

    void ReportJobDetailsWithLabelAndErrcode(
        const std::tuple<JobLogLevel::type, SubJobStatus::type, const int>& reportInfo,
        const std::string& logLabel, const int64_t errCode, const std::string& message);

    template<typename... Args>
    void ReportJobDetails(const JobLogLevel::type &logLevel, SubJobStatus::type jobStatus,
        int jobProgress, Args... logArgs)
    {
        ReportJobDetailsWithLabelAndErrcode(logLevel, jobStatus, jobProgress, "", 0, logArgs...);
    }

private:
    /**
     * Create/query/delete shared resource using framwwork APIs
     */

    /**
     * Get Data_Trans_Frame control device info for REST communication with Protected NAS servers
     */
    static Module::ControlDeviceInfo GetDeviceInfo(ProtectedNasHost nasHost, ProtectedNasShare nasShare, int& ret);

    /**
    * Translate the vendor type string passed in job req by PM/DME_UBC to the enum as required by data-trans-frame
    */
    static Module::STORAGE_ENUM GetVendorType(const std::string inputVendorStr);

    /**
    * Translate the protocol type passed in job req by PM/DME_UBC to the enum as required by data-trans-frame
    */
    static Module::PROTOCOL GetProtocolType(std::string protocolType);

    /**
    * Clone a new FS from the supplied snapshot
    */
    static bool CloneFsFromSnapshot(ProtectedNasHost nasHost, ProtectedNasShare nasShare,
        std::string clonedFsName, int64_t &homoErrCode);

    /**
    * Delete the snapshot and the corresponding cloned FS from the snapshot
    */
    static bool DeleteSnapshotAndClonedFs(ProtectedNasHost nasHost,
        ProtectedNasShare nasShare, std::string snapshotName);

    // utility API to create unique File Name
    static std::string CreateUniqueTempFileName(std::string path);

    // 如果是NetApp的NSF或FusionStore则不需要创建克隆
    static bool NoNeedToCloneFs(const ProtectedNasHost &nasHost, const ProtectedNasShare &nasShare);

    // 插件配置文件读写锁
    static std::mutex m_pluginAtrributeJsonFileMutex;
};

#endif // #ifndef _NAS_SHARE_HETRO_COMMON_SERVICE_H